#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date    : 2013-06-21 10:43:26
# @Author  : vfasky (vfasky@gmail.com)
# @Link    : http://vfasky.com
# @Version : $Id$

import subprocess
import sys
import os
import platform
import requests
import time

__all__ = [
    'redmine_http',
    'get_redmine_user',
    'get_redmine_issues',
    'get_issue',
    'save_issues',
    'post_time_entries',
    '_',
    'cmd',
    'format_svn_info',
    'format_svn_log',
    'get_version_info',
    'get_control_dir',
    'get_last_changed',
    'save_version',
    'set_config',
]

svn_path     = '/Users/vfasky/web/en_cantonfair/'
# 控制文件所在目录
control_path = '/Users/vfasky/web/svn-control/en_cantonfair/'
# redmine 对应的apikey
redmine_key  = ''
redmine_host = 'http://192.168.10.28'
file_prefix  = 'http://192.168.10.7/svn/cantonpm/tags/en_cantonfair/build4prd'
sys_str      = platform.system()
redmine_user = {}
issues       = []
def set_config(**args):
    global svn_path, control_path, redmine_key, redmine_host,\
           file_prefix, redmine_user, issues

    if args.has_key('svn_path'):
        svn_path = args['svn_path']
    if args.has_key('control_path'):
        control_path = args['control_path']
    if args.has_key('redmine_key'):
        redmine_key = args['redmine_key']
    if args.has_key('redmine_host'):
        redmine_host = args['redmine_host']
    if args.has_key('file_prefix'):
        file_prefix = args['file_prefix']

    control_path = os.path.abspath(control_path)
    svn_path     = os.path.abspath(svn_path)

    redmine_user = get_redmine_user()
    issues = []
    for issue in get_redmine_issues(redmine_user['id']):
        issues.append(issue)


def redmine_http(url, data={}, headers={}, type='get'):
    # 封装 redmine http 请求
    _headers = {
        'X-Redmine-API-Key': redmine_key
    }
    headers.update(_headers)
    url = '%s%s' % (redmine_host, url)

    if hasattr(requests, type):
        return getattr(requests, type)(url, data=data, headers=headers)
    return None

def get_redmine_user():
    # 取 redmine 当前用户
    r = redmine_http('/users/current.json')
    return r.json()['user']

def get_redmine_issues(user_id=None):
    # 取 指定用户的 issues
    data = {
        'limit': 25,
        'offset': 0
    }
    if user_id:
        data['assigned_to_id'] = user_id
    r = redmine_http('/issues.json', data=data)
    return r.json()['issues']

def get_issue(id):
    r = redmine_http('/issues/%s.json' % id)
    return r.json()['issue']

def save_issues(id, data):
    # 更新 issues
    custom_field = ''
    if data.has_key('custom_field_values'):
        custom_field = '''
            "custom_field_values" : {"1" : "RC-1"},
        '''
    json_data = '''{
        "issue" : {
            "status_id" : 3 ,
            %s
            "notes" : "%s" 
        }
    }''' % (custom_field, data['notes'].replace('\r','\\r').replace('\n','\\n'))

    r = redmine_http('/issues/%s.json' % id, 
        headers={'Content-Type': 'application/json'},
        data=json_data, 
        type='put')

def post_time_entries(id, data):
    # 添加工时
    t = time.mktime(time.localtime())
    _data = {
        'issue_id': id,
        'spent_on': time.strftime('%Y-%m-%d', time.localtime( float( t ) ) ),
        'activity_id': 9
    }
    _data.update(data)

    xml = []
    for k in _data.keys():
        xml.append('<%s>%s</%s>' % (k, _data[k], k))

    xml_str = "<time_entry>\n%s\n</time_entry>" % '\n'.join(xml)
  
    r = redmine_http('/time_entries.xml', 
        headers={'Content-Type': 'application/xml'},
        data=xml_str, 
        type='post')

    return int(r.status_code) == 201
    

def _(txt):
    # 终端下的中文输出
    if(sys_str =="Windows"):
        try:
            return unicode(txt).decode('utf-8').encode('gbk')
        except:
            return txt
    return txt

def cmd(shell, cwd=None):
    # 与系统交互
    return subprocess.Popen(shell, cwd=cwd, shell=True,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE, 
                            universal_newlines=True)

def format_svn_info(out):
    # 格式化 svn info
    data = {}
    arr  = out.split('\n')
    for v in arr:
        v = v.replace('\r', '')
        line = v.split(':')
        if len(line) == 2:
            data[ line[0].strip() ] = line[1].strip()

    return data

def format_svn_log(out):
    # 格式化 svn 版本 log
    arr        = out.split('\n')
    code       = 0
    paths      = []
    redmine_id = 0
    hours      = 0 #工时
    for v in arr:
        v = v.replace('\r', '')
        if code == 0:
            if v.find('Changed paths') >= 0:
                code = 1
        elif code == 1:
            v = v.strip()
            if v != '' :
                paths.append(v)
            else:
                code = 2
        elif code == 2:
            if v.find('#') >= 0:
                redmine_id = v.split('#')[1].split(' ')[0]

            #工时
            if v.find('@time') >= 0:
                for t in v.split('@time')[1].split(' '):
                    if str(t).isdigit():
                        hours = t
                        break

                if hours == 0:
                    for t in v.split('@time')[1].split('\n'):
                        if str(t).isdigit():
                            hours = t
                            break 

    # 格式化路径
    svn_paths = []
    for v in paths:
        c_type = v.split(' ')[0].strip()
        split_tag = svn_path.replace('\\', '/').split('/').pop()
        path = v.split(split_tag).pop().replace('\\', '/').strip() 
        path = "%s %s%s\r\n" % (c_type, file_prefix, path)
        svn_paths.append(path)

    return {
        'name' : '%s.txt' % redmine_id ,
        'redmine_id' : redmine_id ,
        'hours' : hours,
        'paths': svn_paths ,
        'out': out
    }


def get_version_info(version):
    # 取版本号详情
    version = version.strip()
    out, err = cmd('svn log -r %s -v' % version, svn_path).communicate()

    if err:
        print _(error)
        quit()
    return format_svn_log(out) 

def get_control_dir():
    # 取控制文件最新目录
    # - 更新svn
    p = cmd('svn up', control_path)
    p.wait()

    last_dir = 0
    for v in os.listdir(control_path):
        if os.path.isdir(os.path.join(control_path, v)) and v.isdigit():
            v = int(v)
            if v > last_dir:
                last_dir = v

    if last_dir == 0:
        print _('取不到控制文件目录下的版本号')
        quit()

    
    return os.path.join(control_path, str(last_dir))


def get_last_changed():
    # 取最后一次更新信息
    # 更新svn
    p = cmd('svn up', svn_path)
    p.wait()

    out, err = cmd('svn info', svn_path).communicate()
    svn_info = format_svn_info(out)

    if err:
        print _(err)
        quit()

    svn_info['version'] = svn_info['Last Changed Rev']
    svn_info['data'] = get_version_info(svn_info['Last Changed Rev'])
    return svn_info


def save_version(versions):
    # 保存这些版本的 svn 到更改到对应的控制文件
    # 更新对应的一个 issue 为已解决
    # 注： 一个 issue 可以有多个svn版本， 但这里的多个svn版本只能一个issue
    versions.sort()

    redmine_id = 0
    paths      = []
    logs       = []
    hours      = 0
    for v in versions:
        info = get_version_info(v)
      
        logs.append(info['out'])
        
        for p in info['paths']:
            if p not in paths:
                paths.append(p)

        if str(info['redmine_id']).isdigit() \
        and int(info['redmine_id']) > redmine_id:
            redmine_id = info['redmine_id']
  
        if str(info['hours']).isdigit():
            hours = hours + int(info['hours'])
  

    if 0 == redmine_id:
        print _('redmine_id 错误！结束')
        quit()

    #检查 redmine id 与 issue 是否配对
    this_issue = None
    for issue in issues:
        if int(issue['id']) == int(redmine_id):
            this_issue = issue
            break

    if not this_issue:
        print _('没有检测到对应的 issue, 结束')
        quit()

    # 控制器所在目录
    control_dir = get_control_dir()

    # 控制文件
    control_file_uri = os.path.join(control_dir, '%s.txt' % redmine_id)

    if os.path.isfile(control_file_uri):
        #文件已经存在,读列表
        control_file = open(control_file_uri)
  
        for path in control_file:
            if path.strip() != '' and path not in paths:
                paths.insert(0, path)
        control_file.close()
    
    # print paths
    # quit()        
    
    #写控制文件
    c_file = file(control_file_uri, 'w')
    c_file.writelines(paths)
    c_file.close()


    p = cmd('svn add %s.txt' % redmine_id, control_dir)
    p.wait()

    out, err = cmd('svn ci -m "fix #%s"' % redmine_id , control_dir).communicate()

    if err:
        print _(err)
        quit()


    data = {
        'status_id': 3,
        'notes': 'svn version： %s <br/> log： <pre>%s</pre>' 
                  % (';'.join(versions), '</pre><pre>'.join(logs))
    }
    #print this_issue['custom_fields'][0]['value']
    if this_issue['custom_fields'][0]['value'] == '':
        data['custom_field_values'] = {"1" : "RC-1"}
  
    save_issues(this_issue['id'], data)

    # 记录工时
    if hours > 0:
        post_time_entries(this_issue['id'], {
            'hours': hours
        })

    print 'success'